Nowadays, music is a big part of people’s life. Lyrics is considered as the soul of music. There are so many kinds of music, like Rock and Metal, but, as for me, I am not familiar with all kinds of genres. So, in this project, I am trying exploring the lyrics and understanding the underlying things behind the music’s genres.

#load all required packages
library(dplyr)
library(tidyverse)
library(tokenizers)
library(ggplot2)
library(ggridges)
library(tidytext)
library(d3heatmap)
library(wordcloud2)
library(tm)
library(grid)
library(data.table)
library(plotly)
#load the data

load("~/Desktop/ADS_Teaching/Projects_StarterCodes/Project1-RNotebook/output/processed_lyrics.RData")
dt_lyrics <- dt_lyrics %>% 
  select(id,year,genre,artist,stemmedwords,lyrics) %>% 
  arrange(year) %>%
  filter(year %in% c(1968:2016),genre != "Not Available",genre != "Other")
dt_lyrics <- 
  dt_lyrics %>% 
  mutate(decade=cut(year,breaks=seq(1965,2025,10)),
         length=count_words(dt_lyrics$lyrics),
         length.sep=cut(length,breaks=seq(0,600,100)))

Exploring the relationship between length and genres

lyrics.length.hist <- 
  dt_lyrics %>% 
  dplyr::group_by(genre) %>%
  dplyr::summarise(song.length = sum(length)/n()) %>%
  ggplot()+
  geom_bar(aes(x=genre,y=song.length),stat="identity")+
  labs(title="Average Length of Lyrics in Each Genre",
       subtitle = "Hiphop has the longest lyrics.",
       xlab="Genre",
       ylab="Length of Lyrics")
lyrics.length.hist


lyrics.length.boxplot <- 
  dt_lyrics %>% 
  dplyr::group_by(genre,year) %>%
  dplyr::summarise(song.length = sum(length)/n()) %>%
  ggplot(aes(x=genre,y=song.length,fill=genre))+
  geom_boxplot(alpha=0.6)+
  coord_flip()+
  labs(title="Average Length of Lyrics in Each Genre",
       subtitle = "Hiphop generally has the longest lyrics.",
       xlab="Genre",
       ylab="Length of Lyrics")
lyrics.length.boxplot



# try to see whether the distribution of length of lyrics is different from each genre or decades
lyrics.length.violin.plot <- 
  dt_lyrics %>% 
  dplyr::group_by(genre,year) %>%
  dplyr::summarise(song.length = sum(length)/n()) %>%
  ggplot(aes(x=year,y=song.length))+
  geom_violin()+
  facet_wrap(~genre)+
  labs( title= "Distribution of lyrics' length is various among different genres and years",
        x = "Decades",
        y = "Lyrics' Length")+
  theme_light()
lyrics.length.violin.plot

From the histogram, we notice that the number of words in hip-hop music is commonly more than that in other genres. Also, in the boxplot, we can see that hip-hop contains the most words and the eletronic has the least. From the violin-plot, it is shown that, the number of words in the genres, such as country, jazz and rock music, is kind of consistent as time goes by. The Eletronic music has the longest range in the number of words. For next step, I am trying to figure out the relationship between the distribution of number of lyrics and the decades, so I pick the Metal Music as the example.

# try to see the length of lyrics of hiphop's distribution
lyrics.hiphop <- 
  dt_lyrics %>%
  filter(genre=="Metal") %>%
  ggplot(aes(x=length.sep,y=decade,group=decade,fill=decade))+
  geom_density_ridges(alpha=0.6,scale=2)+
  labs(title = "Number of Words in Metal Music's lyrics' Distribution from 1995 to 2025",
       subtitle = "As time goes by, the number of words in Indie musics' lyrics is 
       more concentrated and the number is increasing",
       x="Length",
       y="Decade",
       fill="Decade")
lyrics.hiphop

According to the ridges plot, there is evident that the length of lyrics is increasing and the distribution became more concentrated as the year goes by.

The analysis of content in lyrics


# the definition of multi_plot
# ggplot objects can be passed in ..., or to plotlist (as a list of ggplot objects)
# - cols:   Number of columns in layout
# - layout: A matrix specifying the layout. If present, 'cols' is ignored.
#
# If the layout is something like matrix(c(1,2,3,3), nrow=2, byrow=TRUE),
# then plot 1 will go in the upper left, 2 will go in the upper right, and
# 3 will go all the way across the bottom.
#
multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) {
  require(grid)

  # Make a list from the ... arguments and plotlist
  plots <- c(list(...), plotlist)

  numPlots = length(plots)

  # If layout is NULL, then use 'cols' to determine layout
  if (is.null(layout)) {
    # Make the panel
    # ncol: Number of columns of plots
    # nrow: Number of rows needed, calculated from # of cols
    layout <- matrix(seq(1, cols * ceiling(numPlots/cols)),
                    ncol = cols, nrow = ceiling(numPlots/cols))
  }

 if (numPlots==1) {
    print(plots[[1]])

  } else {
    # Set up the page
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout))))

    # Make each plot, in the correct location
    for (i in 1:numPlots) {
      # Get the i,j matrix positions of the regions that contain this subplot
      matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE))

      print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row,
                                      layout.pos.col = matchidx$col))
    }
  }
}

# the top10 popular words

rock.top10<-
  dt_lyrics %>%
  dplyr::filter(genre == "Rock") %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  dplyr::group_by(genre,word) %>%
  dplyr::summarise(count=n()) %>%
  top_n(n=10) %>%
  ggplot() +
  geom_bar(aes(x=word,y=count),stat="identity")+
  labs(x="Rock",
       y=" ")
Selecting by count
pop.top10<-
  dt_lyrics %>%
  dplyr::filter(genre == "Pop") %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  dplyr::group_by(genre,word) %>%
  dplyr::summarise(count=n()) %>%
  top_n(n=10) %>%
  ggplot() +
  geom_bar(aes(x=word,y=count),stat="identity")+
  labs(x="Pop",
       y=" ")
Selecting by count
RandB.top10<-
  dt_lyrics %>%
  dplyr::filter(genre == "R&B") %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  dplyr::group_by(genre,word) %>%
  dplyr::summarise(count=n()) %>%
  top_n(n=10) %>%
  ggplot() +
  geom_bar(aes(x=word,y=count),stat="identity")+
  labs(x="R&B",
       y=" ")
Selecting by count
jazz.top10<-
  dt_lyrics %>%
  dplyr::filter(genre == "Jazz") %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  dplyr::group_by(genre,word) %>%
  dplyr::summarise(count=n()) %>%
  top_n(n=10) %>%
  ggplot() +
  geom_bar(aes(x=word,y=count),stat="identity")+
  labs(x="Jazz",
       y=" ")
Selecting by count
country.top10<-
  dt_lyrics %>%
  dplyr::filter(genre == "Country") %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  dplyr::group_by(genre,word) %>%
  dplyr::summarise(count=n()) %>%
  top_n(n=10) %>%
  ggplot() +
  geom_bar(aes(x=word,y=count),stat="identity")+
  labs(x="Country",
       y=" ")
Selecting by count
folk.top10<-
  dt_lyrics %>%
  dplyr::filter(genre == "Folk") %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  dplyr::group_by(genre,word) %>%
  dplyr::summarise(count=n()) %>%
  top_n(n=10) %>%
  ggplot() +
  geom_bar(aes(x=word,y=count),stat="identity")+
  labs(x="Folk",
       y=" ")
Selecting by count
electronic.top10<-
  dt_lyrics %>%
  dplyr::filter(genre == "Electronic") %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  dplyr::group_by(genre,word) %>%
  dplyr::summarise(count=n()) %>%
  top_n(n=10) %>%
  ggplot() +
  geom_bar(aes(x=word,y=count),stat="identity")+
  labs(x="Electronic",
       y=" ")
Selecting by count
metal.top10<-
  dt_lyrics %>%
  dplyr::filter(genre == "Metal") %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  dplyr::group_by(genre,word) %>%
  dplyr::summarise(count=n()) %>%
  top_n(n=10) %>%
  ggplot() +
  geom_bar(aes(x=word,y=count),stat="identity")+
  labs(x="Metal",
       y=" ")
Selecting by count
hiphop.top10<-
  dt_lyrics %>%
  dplyr::filter(genre == "Hip-Hop") %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  dplyr::group_by(genre,word) %>%
  dplyr::summarise(count=n()) %>%
  top_n(n=10) %>%
  ggplot() +
 geom_bar(aes(x=word,y=count),stat="identity")+
  labs(x="Hip-Hop",
       y=" ")
Selecting by count
indie.top10<-
  dt_lyrics %>%
  dplyr::filter(genre == "Indie") %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  dplyr::group_by(genre,word) %>%
  dplyr::summarise(count=n()) %>%
  top_n(n=10) %>%
  ggplot() +
  geom_bar(aes(x=word,y=count),stat="identity")+
  labs(x="Indie",
       y=" ")
Selecting by count
multiplot(rock.top10,pop.top10,RandB.top10,jazz.top10,country.top10,folk.top10,electronic.top10,metal.top10,hiphop.top10,indie.top10,cols=2)
Loading required package: grid

As we can see, except for Metal, love is used the most frequently by all the genres, and word “love” is mentioned over 80000 times, which is the highest in all genres. However, in metal music, the terms like “life” and “time”, are mentioned more than the term “love”. I guess this is resulted by the core of metal music, the aggressive attitude about life rather than the joy of the world. We also can conclude this from the word cloud of metal music’s lyrics.

metal.cloud <- 
  dt_lyrics %>%
  filter(genre=="Metal") %>%
  select(stemmedwords) %>% 
  VectorSource() %>%
  VCorpus()
dtm.metal <- DocumentTermMatrix(metal.cloud)
word.metal <- colnames(as.matrix(dtm.metal))
cloud.metal <- data.frame(word=word.metal,
                         freq=apply(as.matrix(dtm.metal), 2, sum))
cloud.metal <- cloud.metal[order(cloud.metal$freq, decreasing=T), ] 
wordcloud2(data=cloud.metal[1:50,], size=0.5,shape="oval")

This wordcloud contains the top 200 frequently used terms in metal music, and we can observe that most of terms express negative feelings like die, pain and lie. Hence, I guess the major emotion of metal music is negative. In order to explore the emotion underlying the metal music, we use the sentiment analysis.

Semtiment analysis

sentiment.list <- get_sentiments("nrc")
metal.sentiment<-
  dt_lyrics %>%
  dplyr::filter(genre=="Metal")%>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  inner_join(sentiment.list,by="word") %>%
  dplyr::group_by(genre,sentiment) %>%
  dplyr::summarise(n=n())

metal.sentiment.decade <- 
  dt_lyrics %>%
  dplyr::filter(genre=="Metal") %>%
  dplyr::select(genre, decade, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  inner_join(sentiment.list,by="word") %>%
  dplyr::group_by(genre,decade,sentiment) %>%
  dplyr::summarise(n=n()) 
metal.sentiment.decade_exc_2005_2015 <-
  metal.sentiment.decade %>%
  filter(decade != "(2005,2015]")

p1 <-
  ggplot(metal.sentiment.decade,aes(x=decade,y=n,fill=sentiment))+
  geom_bar(stat="identity")

p2 <-
  ggplot(metal.sentiment.decade_exc_2005_2015,aes(x=decade,y=n,fill=sentiment))+
  geom_bar(stat="identity")
multiplot(p1,p2,cols=1)

NA

As we can see, the top 5 major feeling of the metal music is negative, positive, fear, sadness and anger, and four of five suggest the darkness of life. Hence, we can infer that the reason why metal music is less often to mention the term “love” is that its major emotion is negative. We also can use the sentiment analysis on all the genres to prove that.

sentiment.all <-
  dt_lyrics %>%
  dplyr::select(genre, stemmedwords) %>% 
  unnest_tokens(word, stemmedwords) %>%
  inner_join(sentiment.list,by="word") %>%
  dplyr::group_by(genre,sentiment) %>%
  dplyr::summarise(n=n()) %>%
  pivot_wider(names_from = "sentiment",values_from = "n" ) %>%
  ungroup()

sentiment.genre <- sentiment.all$genre
sentiments <- sentiment.all %>% select(-genre) %>% as.matrix()
sentiment.hmp <- diag(1/(apply(sentiment,1,sum))) %*% sentiments
rownames(sentiment.hmp) <- sentiment.genre
d3heatmap(sentiment.hmp,color = "Blues", Rowv=T, Colv=F)


# trying to figure out which sentiment the term love is corresponding
sentiment.list$sentiment[which(sentiment.list$word=="love")]
[1] "joy"      "positive"

As the result shown in the sentiment analysis, the term ‘love’ is corresponding to joy and positive emotion, and also considering what the heatmap is shown,compared to other genres, the metal music has more preference on the negative emotion rather than the joyful and positive emotions. Therefore, the metal music relatively rarely mentions the term love.

The distribution of metal artists

I wanna find out the distribution of metal music as decades goes by

dt_artist <- fread('../data/artists.csv')

metal.origin.1975to1985 <- 
  dt_lyrics %>% 
  left_join(dt_artist,c("artist"="Artist")) %>% 
  filter(genre=="Metal",decade=="(1975,1985]",Origin != "") %>% 
  group_by(genre,Origin) %>%
  summarise(n=n())
metal.origin.1985to1995 <- 
  dt_lyrics %>% 
  left_join(dt_artist,c("artist"="Artist")) %>% 
  filter(genre=="Metal",decade=="(1985,1995]",Origin != "") %>% 
  group_by(genre,Origin) %>%
  summarise(n=n())%>%
  arrange(desc(n))
metal.origin.1995to2005  <-
  dt_lyrics %>% 
  left_join(dt_artist,c("artist"="Artist")) %>% 
  filter(genre=="Metal",decade=="(1995,2005]",Origin != "") %>% 
  group_by(genre,Origin) %>%
  summarise(n=n())
metal.origin.2005to2015  <-
  dt_lyrics %>% 
  left_join(dt_artist,c("artist"="Artist")) %>% 
  filter(genre=="Metal",decade=="(2005,2015]",Origin != "") %>% 
  group_by(genre,Origin) %>%
  summarise(n=n())%>%
  arrange(desc(n))
metal.origin.2015to2025 <-
  dt_lyrics %>% 
  left_join(dt_artist,c("artist"="Artist")) %>% 
  filter(genre=="Metal",decade=="(2015,2025]",Origin != "") %>% 
  group_by(genre,Origin) %>%
  summarise(n=n())%>%
  arrange(desc(n))
plot_ly(metal.origin.1975to1985,labels=~Origin,values=~n,textinfo="none") %>%
  add_pie()

plot_ly(metal.origin.1985to1995,labels=~Origin,values=~n,textinfo="none") %>%
  add_pie()

plot_ly(metal.origin.1995to2005,labels=~Origin,values=~n,textinfo="none") %>%
  add_pie()

plot_ly(metal.origin.2005to2015,labels=~Origin,values=~n,textinfo="none") %>%
  add_pie()

plot_ly(metal.origin.2015to2025,labels=~Origin,values=~n,textinfo="none") %>%
  add_pie()

NA

As we can see from the pie chart, the first metal music was created in Sweden. And during 1985 to 1995, most of metal artist were still from Sweden, with a few of them came from the United States. During 1995 to 2005, the metal music was spread to other European coutries, such as Finland and England. After 2005, the metal music is spread around Europe, North America.

conclusion

1 Hip-Hop commonly contains the longer words compared to other genres. The number of words in metal music seems to increase as time goes by.

2 The reason why the metal music is less often to mention the term “love” is that its major emotion is more about the darkness and negative feelings about the world.

3 Most of metal artists are from Sweden.

LS0tCnRpdGxlOiAiTHlyaWNzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpOb3dhZGF5cywgbXVzaWMgaXMgYSBiaWcgcGFydCBvZiBwZW9wbGUncyBsaWZlLiBMeXJpY3MgaXMgY29uc2lkZXJlZCBhcyB0aGUgc291bCBvZiBtdXNpYy4gVGhlcmUgYXJlIHNvIG1hbnkga2luZHMgb2YgbXVzaWMsIGxpa2UgUm9jayBhbmQgTWV0YWwsIGJ1dCwgYXMgZm9yIG1lLCBJIGFtIG5vdCBmYW1pbGlhciB3aXRoIGFsbCBraW5kcyBvZiBnZW5yZXMuIFNvLCAgaW4gdGhpcyBwcm9qZWN0LCBJIGFtIHRyeWluZyBleHBsb3JpbmcgdGhlIGx5cmljcyBhbmQgdW5kZXJzdGFuZGluZyB0aGUgdW5kZXJseWluZyB0aGluZ3MgYmVoaW5kIHRoZSBtdXNpYydzIGdlbnJlcy4KCgpgYGB7cn0KI2xvYWQgYWxsIHJlcXVpcmVkIHBhY2thZ2VzCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRva2VuaXplcnMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JpZGdlcykKbGlicmFyeSh0aWR5dGV4dCkKbGlicmFyeShkM2hlYXRtYXApCmxpYnJhcnkod29yZGNsb3VkMikKbGlicmFyeSh0bSkKbGlicmFyeShncmlkKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkocGxvdGx5KQpgYGAKCgpgYGB7cn0KI2xvYWQgdGhlIGRhdGEKCmxvYWQoIn4vRGVza3RvcC9BRFNfVGVhY2hpbmcvUHJvamVjdHNfU3RhcnRlckNvZGVzL1Byb2plY3QxLVJOb3RlYm9vay9vdXRwdXQvcHJvY2Vzc2VkX2x5cmljcy5SRGF0YSIpCmR0X2x5cmljcyA8LSBkdF9seXJpY3MgJT4lIAogIHNlbGVjdChpZCx5ZWFyLGdlbnJlLGFydGlzdCxzdGVtbWVkd29yZHMsbHlyaWNzKSAlPiUgCiAgYXJyYW5nZSh5ZWFyKSAlPiUKICBmaWx0ZXIoeWVhciAlaW4lIGMoMTk2ODoyMDE2KSxnZW5yZSAhPSAiTm90IEF2YWlsYWJsZSIsZ2VucmUgIT0gIk90aGVyIikKZHRfbHlyaWNzIDwtIAogIGR0X2x5cmljcyAlPiUgCiAgbXV0YXRlKGRlY2FkZT1jdXQoeWVhcixicmVha3M9c2VxKDE5NjUsMjAyNSwxMCkpLAogICAgICAgICBsZW5ndGg9Y291bnRfd29yZHMoZHRfbHlyaWNzJGx5cmljcyksCiAgICAgICAgIGxlbmd0aC5zZXA9Y3V0KGxlbmd0aCxicmVha3M9c2VxKDAsNjAwLDEwMCkpKQoKYGBgCgojIyBFeHBsb3JpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGxlbmd0aCBhbmQgZ2VucmVzCgpgYGB7cn0KbHlyaWNzLmxlbmd0aC5oaXN0IDwtIAogIGR0X2x5cmljcyAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGdlbnJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKHNvbmcubGVuZ3RoID0gc3VtKGxlbmd0aCkvbigpKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2JhcihhZXMoeD1nZW5yZSx5PXNvbmcubGVuZ3RoKSxzdGF0PSJpZGVudGl0eSIpKwogIGxhYnModGl0bGU9IkF2ZXJhZ2UgTGVuZ3RoIG9mIEx5cmljcyBpbiBFYWNoIEdlbnJlIiwKICAgICAgIHN1YnRpdGxlID0gIkhpcGhvcCBoYXMgdGhlIGxvbmdlc3QgbHlyaWNzLiIsCiAgICAgICB4bGFiPSJHZW5yZSIsCiAgICAgICB5bGFiPSJMZW5ndGggb2YgTHlyaWNzIikKbHlyaWNzLmxlbmd0aC5oaXN0CgpseXJpY3MubGVuZ3RoLmJveHBsb3QgPC0gCiAgZHRfbHlyaWNzICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZ2VucmUseWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShzb25nLmxlbmd0aCA9IHN1bShsZW5ndGgpL24oKSkgJT4lCiAgZ2dwbG90KGFlcyh4PWdlbnJlLHk9c29uZy5sZW5ndGgsZmlsbD1nZW5yZSkpKwogIGdlb21fYm94cGxvdChhbHBoYT0wLjYpKwogIGNvb3JkX2ZsaXAoKSsKICBsYWJzKHRpdGxlPSJBdmVyYWdlIExlbmd0aCBvZiBMeXJpY3MgaW4gRWFjaCBHZW5yZSIsCiAgICAgICBzdWJ0aXRsZSA9ICJIaXBob3AgZ2VuZXJhbGx5IGhhcyB0aGUgbG9uZ2VzdCBseXJpY3MuIiwKICAgICAgIHhsYWI9IkdlbnJlIiwKICAgICAgIHlsYWI9Ikxlbmd0aCBvZiBMeXJpY3MiKQpseXJpY3MubGVuZ3RoLmJveHBsb3QKCgojIHRyeSB0byBzZWUgd2hldGhlciB0aGUgZGlzdHJpYnV0aW9uIG9mIGxlbmd0aCBvZiBseXJpY3MgaXMgZGlmZmVyZW50IGZyb20gZWFjaCBnZW5yZSBvciBkZWNhZGVzCmx5cmljcy5sZW5ndGgudmlvbGluLnBsb3QgPC0gCiAgZHRfbHlyaWNzICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZ2VucmUseWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShzb25nLmxlbmd0aCA9IHN1bShsZW5ndGgpL24oKSkgJT4lCiAgZ2dwbG90KGFlcyh4PXllYXIseT1zb25nLmxlbmd0aCkpKwogIGdlb21fdmlvbGluKCkrCiAgZmFjZXRfd3JhcCh+Z2VucmUpKwogIGxhYnMoIHRpdGxlPSAiRGlzdHJpYnV0aW9uIG9mIGx5cmljcycgbGVuZ3RoIGlzIHZhcmlvdXMgYW1vbmcgZGlmZmVyZW50IGdlbnJlcyBhbmQgeWVhcnMiLAogICAgICAgIHggPSAiRGVjYWRlcyIsCiAgICAgICAgeSA9ICJMeXJpY3MnIExlbmd0aCIpKwogIHRoZW1lX2xpZ2h0KCkKbHlyaWNzLmxlbmd0aC52aW9saW4ucGxvdAoKYGBgCgpGcm9tIHRoZSBoaXN0b2dyYW0sIHdlIG5vdGljZSB0aGF0IHRoZSBudW1iZXIgb2Ygd29yZHMgaW4gaGlwLWhvcCBtdXNpYyBpcyBjb21tb25seSBtb3JlIHRoYW4gdGhhdCBpbiBvdGhlciBnZW5yZXMuIEFsc28sIGluIHRoZSBib3hwbG90LCB3ZSBjYW4gc2VlIHRoYXQgaGlwLWhvcCBjb250YWlucyB0aGUgbW9zdCB3b3JkcyBhbmQgdGhlIGVsZXRyb25pYyBoYXMgdGhlIGxlYXN0LiBGcm9tIHRoZSB2aW9saW4tcGxvdCwgaXQgaXMgc2hvd24gdGhhdCwgdGhlIG51bWJlciBvZiB3b3JkcyBpbiB0aGUgZ2VucmVzLCBzdWNoIGFzIGNvdW50cnksIGphenogYW5kIHJvY2sgbXVzaWMsIGlzIGtpbmQgb2YgY29uc2lzdGVudCBhcyB0aW1lIGdvZXMgYnkuIFRoZSBFbGV0cm9uaWMgbXVzaWMgaGFzIHRoZSBsb25nZXN0IHJhbmdlIGluIHRoZSBudW1iZXIgb2Ygd29yZHMuCkZvciBuZXh0IHN0ZXAsIEkgYW0gdHJ5aW5nIHRvIGZpZ3VyZSBvdXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBkaXN0cmlidXRpb24gb2YgbnVtYmVyIG9mIGx5cmljcyBhbmQgdGhlIGRlY2FkZXMsIHNvIEkgcGljayB0aGUgTWV0YWwgTXVzaWMgYXMgdGhlIGV4YW1wbGUuCgoKCmBgYHtyfQojIHRyeSB0byBzZWUgdGhlIGxlbmd0aCBvZiBseXJpY3Mgb2YgaGlwaG9wJ3MgZGlzdHJpYnV0aW9uCmx5cmljcy5tZXRhbCA8LSAKICBkdF9seXJpY3MgJT4lCiAgZmlsdGVyKGdlbnJlPT0iTWV0YWwiKSAlPiUKICBkcm9wX25hKCklPiUKICBnZ3Bsb3QoYWVzKHg9bGVuZ3RoLnNlcCx5PWRlY2FkZSxncm91cD1kZWNhZGUsZmlsbD1kZWNhZGUpKSsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFscGhhPTAuNixzY2FsZT0yKSsKICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBXb3JkcyBpbiBNZXRhbCBNdXNpYydzIGx5cmljcycgRGlzdHJpYnV0aW9uIGZyb20gMTk5NSB0byAyMDI1IiwKICAgICAgIHN1YnRpdGxlID0gIkFzIHRpbWUgZ29lcyBieSwgdGhlIG51bWJlciBvZiB3b3JkcyBpbiBJbmRpZSBtdXNpY3MnIGx5cmljcyBpcyAKICAgICAgIG1vcmUgY29uY2VudHJhdGVkIGFuZCB0aGUgbnVtYmVyIGlzIGluY3JlYXNpbmciLAogICAgICAgeD0iTGVuZ3RoIiwKICAgICAgIHk9IkRlY2FkZSIsCiAgICAgICBmaWxsPSJEZWNhZGUiKQpseXJpY3MubWV0YWwKYGBgCkFjY29yZGluZyB0byB0aGUgcmlkZ2VzIHBsb3QsIHRoZXJlIGlzIGV2aWRlbnQgdGhhdCB0aGUgbGVuZ3RoIG9mIGx5cmljcyBpcyBpbmNyZWFzaW5nIGFuZCB0aGUgZGlzdHJpYnV0aW9uIGJlY2FtZSBtb3JlIGNvbmNlbnRyYXRlZCBhcyB0aGUgeWVhciBnb2VzIGJ5LgoKIyMgVGhlIGFuYWx5c2lzIG9mIGNvbnRlbnQgaW4gbHlyaWNzCgpgYGB7cn0KCiMgdGhlIGRlZmluaXRpb24gb2YgbXVsdGlfcGxvdAojIGdncGxvdCBvYmplY3RzIGNhbiBiZSBwYXNzZWQgaW4gLi4uLCBvciB0byBwbG90bGlzdCAoYXMgYSBsaXN0IG9mIGdncGxvdCBvYmplY3RzKQojIC0gY29sczogICBOdW1iZXIgb2YgY29sdW1ucyBpbiBsYXlvdXQKIyAtIGxheW91dDogQSBtYXRyaXggc3BlY2lmeWluZyB0aGUgbGF5b3V0LiBJZiBwcmVzZW50LCAnY29scycgaXMgaWdub3JlZC4KIwojIElmIHRoZSBsYXlvdXQgaXMgc29tZXRoaW5nIGxpa2UgbWF0cml4KGMoMSwyLDMsMyksIG5yb3c9MiwgYnlyb3c9VFJVRSksCiMgdGhlbiBwbG90IDEgd2lsbCBnbyBpbiB0aGUgdXBwZXIgbGVmdCwgMiB3aWxsIGdvIGluIHRoZSB1cHBlciByaWdodCwgYW5kCiMgMyB3aWxsIGdvIGFsbCB0aGUgd2F5IGFjcm9zcyB0aGUgYm90dG9tLgojCm11bHRpcGxvdCA8LSBmdW5jdGlvbiguLi4sIHBsb3RsaXN0PU5VTEwsIGZpbGUsIGNvbHM9MSwgbGF5b3V0PU5VTEwpIHsKICByZXF1aXJlKGdyaWQpCgogICMgTWFrZSBhIGxpc3QgZnJvbSB0aGUgLi4uIGFyZ3VtZW50cyBhbmQgcGxvdGxpc3QKICBwbG90cyA8LSBjKGxpc3QoLi4uKSwgcGxvdGxpc3QpCgogIG51bVBsb3RzID0gbGVuZ3RoKHBsb3RzKQoKICAjIElmIGxheW91dCBpcyBOVUxMLCB0aGVuIHVzZSAnY29scycgdG8gZGV0ZXJtaW5lIGxheW91dAogIGlmIChpcy5udWxsKGxheW91dCkpIHsKICAgICMgTWFrZSB0aGUgcGFuZWwKICAgICMgbmNvbDogTnVtYmVyIG9mIGNvbHVtbnMgb2YgcGxvdHMKICAgICMgbnJvdzogTnVtYmVyIG9mIHJvd3MgbmVlZGVkLCBjYWxjdWxhdGVkIGZyb20gIyBvZiBjb2xzCiAgICBsYXlvdXQgPC0gbWF0cml4KHNlcSgxLCBjb2xzICogY2VpbGluZyhudW1QbG90cy9jb2xzKSksCiAgICAgICAgICAgICAgICAgICAgbmNvbCA9IGNvbHMsIG5yb3cgPSBjZWlsaW5nKG51bVBsb3RzL2NvbHMpKQogIH0KCiBpZiAobnVtUGxvdHM9PTEpIHsKICAgIHByaW50KHBsb3RzW1sxXV0pCgogIH0gZWxzZSB7CiAgICAjIFNldCB1cCB0aGUgcGFnZQogICAgZ3JpZC5uZXdwYWdlKCkKICAgIHB1c2hWaWV3cG9ydCh2aWV3cG9ydChsYXlvdXQgPSBncmlkLmxheW91dChucm93KGxheW91dCksIG5jb2wobGF5b3V0KSkpKQoKICAgICMgTWFrZSBlYWNoIHBsb3QsIGluIHRoZSBjb3JyZWN0IGxvY2F0aW9uCiAgICBmb3IgKGkgaW4gMTpudW1QbG90cykgewogICAgICAjIEdldCB0aGUgaSxqIG1hdHJpeCBwb3NpdGlvbnMgb2YgdGhlIHJlZ2lvbnMgdGhhdCBjb250YWluIHRoaXMgc3VicGxvdAogICAgICBtYXRjaGlkeCA8LSBhcy5kYXRhLmZyYW1lKHdoaWNoKGxheW91dCA9PSBpLCBhcnIuaW5kID0gVFJVRSkpCgogICAgICBwcmludChwbG90c1tbaV1dLCB2cCA9IHZpZXdwb3J0KGxheW91dC5wb3Mucm93ID0gbWF0Y2hpZHgkcm93LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheW91dC5wb3MuY29sID0gbWF0Y2hpZHgkY29sKSkKICAgIH0KICB9Cn0KCiMgdGhlIHRvcDEwIHBvcHVsYXIgd29yZHMKCnJvY2sudG9wMTA8LQogIGR0X2x5cmljcyAlPiUKICBkcGx5cjo6ZmlsdGVyKGdlbnJlID09ICJSb2NrIikgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5yZSwgc3RlbW1lZHdvcmRzKSAlPiUgCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCBzdGVtbWVkd29yZHMpICU+JQogIGRwbHlyOjpncm91cF9ieShnZW5yZSx3b3JkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50PW4oKSkgJT4lCiAgdG9wX24obj0xMCkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYmFyKGFlcyh4PXdvcmQseT1jb3VudCksc3RhdD0iaWRlbnRpdHkiKSsKICBsYWJzKHg9IlJvY2siLAogICAgICAgeT0iICIpCgpwb3AudG9wMTA8LQogIGR0X2x5cmljcyAlPiUKICBkcGx5cjo6ZmlsdGVyKGdlbnJlID09ICJQb3AiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbnJlLCBzdGVtbWVkd29yZHMpICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHN0ZW1tZWR3b3JkcykgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbnJlLHdvcmQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKSAlPiUKICB0b3BfbihuPTEwKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIoYWVzKHg9d29yZCx5PWNvdW50KSxzdGF0PSJpZGVudGl0eSIpKwogIGxhYnMoeD0iUG9wIiwKICAgICAgIHk9IiAiKQoKUmFuZEIudG9wMTA8LQogIGR0X2x5cmljcyAlPiUKICBkcGx5cjo6ZmlsdGVyKGdlbnJlID09ICJSJkIiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbnJlLCBzdGVtbWVkd29yZHMpICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHN0ZW1tZWR3b3JkcykgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbnJlLHdvcmQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKSAlPiUKICB0b3BfbihuPTEwKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIoYWVzKHg9d29yZCx5PWNvdW50KSxzdGF0PSJpZGVudGl0eSIpKwogIGxhYnMoeD0iUiZCIiwKICAgICAgIHk9IiAiKQoKamF6ei50b3AxMDwtCiAgZHRfbHlyaWNzICU+JQogIGRwbHlyOjpmaWx0ZXIoZ2VucmUgPT0gIkphenoiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbnJlLCBzdGVtbWVkd29yZHMpICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHN0ZW1tZWR3b3JkcykgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbnJlLHdvcmQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKSAlPiUKICB0b3BfbihuPTEwKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIoYWVzKHg9d29yZCx5PWNvdW50KSxzdGF0PSJpZGVudGl0eSIpKwogIGxhYnMoeD0iSmF6eiIsCiAgICAgICB5PSIgIikKCmNvdW50cnkudG9wMTA8LQogIGR0X2x5cmljcyAlPiUKICBkcGx5cjo6ZmlsdGVyKGdlbnJlID09ICJDb3VudHJ5IikgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5yZSwgc3RlbW1lZHdvcmRzKSAlPiUgCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCBzdGVtbWVkd29yZHMpICU+JQogIGRwbHlyOjpncm91cF9ieShnZW5yZSx3b3JkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50PW4oKSkgJT4lCiAgdG9wX24obj0xMCkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYmFyKGFlcyh4PXdvcmQseT1jb3VudCksc3RhdD0iaWRlbnRpdHkiKSsKICBsYWJzKHg9IkNvdW50cnkiLAogICAgICAgeT0iICIpCgpmb2xrLnRvcDEwPC0KICBkdF9seXJpY3MgJT4lCiAgZHBseXI6OmZpbHRlcihnZW5yZSA9PSAiRm9sayIpICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VucmUsIHN0ZW1tZWR3b3JkcykgJT4lIAogIHVubmVzdF90b2tlbnMod29yZCwgc3RlbW1lZHdvcmRzKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZ2VucmUsd29yZCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudD1uKCkpICU+JQogIHRvcF9uKG49MTApICU+JQogIGdncGxvdCgpICsKICBnZW9tX2JhcihhZXMoeD13b3JkLHk9Y291bnQpLHN0YXQ9ImlkZW50aXR5IikrCiAgbGFicyh4PSJGb2xrIiwKICAgICAgIHk9IiAiKQoKZWxlY3Ryb25pYy50b3AxMDwtCiAgZHRfbHlyaWNzICU+JQogIGRwbHlyOjpmaWx0ZXIoZ2VucmUgPT0gIkVsZWN0cm9uaWMiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbnJlLCBzdGVtbWVkd29yZHMpICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHN0ZW1tZWR3b3JkcykgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbnJlLHdvcmQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKSAlPiUKICB0b3BfbihuPTEwKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIoYWVzKHg9d29yZCx5PWNvdW50KSxzdGF0PSJpZGVudGl0eSIpKwogIGxhYnMoeD0iRWxlY3Ryb25pYyIsCiAgICAgICB5PSIgIikKCm1ldGFsLnRvcDEwPC0KICBkdF9seXJpY3MgJT4lCiAgZHBseXI6OmZpbHRlcihnZW5yZSA9PSAiTWV0YWwiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbnJlLCBzdGVtbWVkd29yZHMpICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHN0ZW1tZWR3b3JkcykgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbnJlLHdvcmQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKSAlPiUKICB0b3BfbihuPTEwKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIoYWVzKHg9d29yZCx5PWNvdW50KSxzdGF0PSJpZGVudGl0eSIpKwogIGxhYnMoeD0iTWV0YWwiLAogICAgICAgeT0iICIpCgpoaXBob3AudG9wMTA8LQogIGR0X2x5cmljcyAlPiUKICBkcGx5cjo6ZmlsdGVyKGdlbnJlID09ICJIaXAtSG9wIikgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5yZSwgc3RlbW1lZHdvcmRzKSAlPiUgCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCBzdGVtbWVkd29yZHMpICU+JQogIGRwbHlyOjpncm91cF9ieShnZW5yZSx3b3JkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50PW4oKSkgJT4lCiAgdG9wX24obj0xMCkgJT4lCiAgZ2dwbG90KCkgKwogZ2VvbV9iYXIoYWVzKHg9d29yZCx5PWNvdW50KSxzdGF0PSJpZGVudGl0eSIpKwogIGxhYnMoeD0iSGlwLUhvcCIsCiAgICAgICB5PSIgIikKCmluZGllLnRvcDEwPC0KICBkdF9seXJpY3MgJT4lCiAgZHBseXI6OmZpbHRlcihnZW5yZSA9PSAiSW5kaWUiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbnJlLCBzdGVtbWVkd29yZHMpICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHN0ZW1tZWR3b3JkcykgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbnJlLHdvcmQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKSAlPiUKICB0b3BfbihuPTEwKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIoYWVzKHg9d29yZCx5PWNvdW50KSxzdGF0PSJpZGVudGl0eSIpKwogIGxhYnMoeD0iSW5kaWUiLAogICAgICAgeT0iICIpCgoKbXVsdGlwbG90KHJvY2sudG9wMTAscG9wLnRvcDEwLFJhbmRCLnRvcDEwLGphenoudG9wMTAsY291bnRyeS50b3AxMCxmb2xrLnRvcDEwLGVsZWN0cm9uaWMudG9wMTAsbWV0YWwudG9wMTAsaGlwaG9wLnRvcDEwLGluZGllLnRvcDEwLGNvbHM9MikKCgpgYGAKQXMgd2UgY2FuIHNlZSwgZXhjZXB0IGZvciBNZXRhbCwgbG92ZSBpcyB1c2VkIHRoZSBtb3N0IGZyZXF1ZW50bHkgYnkgYWxsIHRoZSBnZW5yZXMsIGFuZCB3b3JkICJsb3ZlIiBpcyBtZW50aW9uZWQgb3ZlciA4MDAwMCB0aW1lcywgd2hpY2ggaXMgdGhlIGhpZ2hlc3QgaW4gYWxsIGdlbnJlcy4gSG93ZXZlciwgaW4gbWV0YWwgbXVzaWMsIHRoZSB0ZXJtcyBsaWtlICJsaWZlIiBhbmQgInRpbWUiLCBhcmUgbWVudGlvbmVkIG1vcmUgdGhhbiB0aGUgdGVybSAibG92ZSIuIEkgZ3Vlc3MgdGhpcyBpcyByZXN1bHRlZCBieSB0aGUgY29yZSBvZiBtZXRhbCBtdXNpYywgdGhlIGFnZ3Jlc3NpdmUgYXR0aXR1ZGUgYWJvdXQgbGlmZSByYXRoZXIgdGhhbiB0aGUgam95IG9mIHRoZSB3b3JsZC4gV2UgYWxzbyBjYW4gY29uY2x1ZGUgdGhpcyBmcm9tIHRoZSB3b3JkIGNsb3VkIG9mIG1ldGFsIG11c2ljJ3MgbHlyaWNzLgoKCmBgYHtyfQptZXRhbDwtCiAgZHRfbHlyaWNzICU+JQogIGRwbHlyOjpmaWx0ZXIoZ2VucmUgPT0gIk1ldGFsIikgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5yZSwgc3RlbW1lZHdvcmRzKSAlPiUgCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCBzdGVtbWVkd29yZHMpICU+JQogIGRwbHlyOjpncm91cF9ieShnZW5yZSx3b3JkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50PW4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHNlbGVjdCgtZ2VucmUpCgp3b3JkY2xvdWQyKG1ldGFsWzE6MjAwLF0sc2l6ZT0wLjUsIGNvbG9yPSJyYW5kb20tZGFyayIpCmBgYAoKVGhpcyB3b3JkY2xvdWQgY29udGFpbnMgdGhlIHRvcCAyMDAgZnJlcXVlbnRseSB1c2VkIHRlcm1zIGluIG1ldGFsIG11c2ljLCBhbmQgd2UgY2FuIG9ic2VydmUgdGhhdCBtb3N0IG9mIHRlcm1zIGV4cHJlc3MgbmVnYXRpdmUgZmVlbGluZ3MgbGlrZSBkaWUsIHBhaW4gYW5kIGxpZS4gSGVuY2UsIEkgZ3Vlc3MgdGhlIG1ham9yIGVtb3Rpb24gb2YgbWV0YWwgbXVzaWMgaXMgbmVnYXRpdmUuIEluIG9yZGVyIHRvIGV4cGxvcmUgdGhlIGVtb3Rpb24gdW5kZXJseWluZyB0aGUgbWV0YWwgbXVzaWMsIHdlIHVzZSB0aGUgc2VudGltZW50IGFuYWx5c2lzLgoKIyMgU2VtdGltZW50IGFuYWx5c2lzCmBgYHtyfQpzZW50aW1lbnQubGlzdCA8LSBnZXRfc2VudGltZW50cygibnJjIikKbWV0YWwuc2VudGltZW50PC0KICBkdF9seXJpY3MgJT4lCiAgZHBseXI6OmZpbHRlcihnZW5yZT09Ik1ldGFsIiklPiUKICBkcGx5cjo6c2VsZWN0KGdlbnJlLCBzdGVtbWVkd29yZHMpICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHN0ZW1tZWR3b3JkcykgJT4lCiAgaW5uZXJfam9pbihzZW50aW1lbnQubGlzdCxieT0id29yZCIpICU+JQogIGRwbHlyOjpncm91cF9ieShnZW5yZSxzZW50aW1lbnQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2Uobj1uKCkpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkKbWV0YWwuc2VudGltZW50LnRvcDUgPC0gbWV0YWwuc2VudGltZW50WzE6NSxdCgptZXRhbC5zZW50aW1lbnQuZGVjYWRlIDwtIAogIGR0X2x5cmljcyAlPiUKICBkcGx5cjo6ZmlsdGVyKGdlbnJlPT0iTWV0YWwiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbnJlLCBkZWNhZGUsIHN0ZW1tZWR3b3JkcykgJT4lIAogIHVubmVzdF90b2tlbnMod29yZCwgc3RlbW1lZHdvcmRzKSAlPiUKICBpbm5lcl9qb2luKHNlbnRpbWVudC5saXN0LGJ5PSJ3b3JkIikgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbnJlLGRlY2FkZSxzZW50aW1lbnQpICU+JQogIGRwbHlyOjpzdW1tYXJpc2Uobj1uKCkpIAptZXRhbC5zZW50aW1lbnQuZGVjYWRlX2V4Y18yMDA1XzIwMTUgPC0KICBtZXRhbC5zZW50aW1lbnQuZGVjYWRlICU+JQogIGZpbHRlcihkZWNhZGUgIT0gIigyMDA1LDIwMTVdIikKCnAxIDwtCiAgZ2dwbG90KG1ldGFsLnNlbnRpbWVudC5kZWNhZGUsYWVzKHg9ZGVjYWRlLHk9bixmaWxsPXNlbnRpbWVudCkpKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikKCnAyIDwtCiAgZ2dwbG90KG1ldGFsLnNlbnRpbWVudC5kZWNhZGVfZXhjXzIwMDVfMjAxNSxhZXMoeD1kZWNhZGUseT1uLGZpbGw9c2VudGltZW50KSkrCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKQptdWx0aXBsb3QocDEscDIsY29scz0xKQogIApgYGAKQXMgd2UgY2FuIHNlZSwgdGhlIHRvcCA1IG1ham9yIGZlZWxpbmcgb2YgdGhlIG1ldGFsIG11c2ljIGlzIG5lZ2F0aXZlLCBwb3NpdGl2ZSwgZmVhciwgc2FkbmVzcyBhbmQgYW5nZXIsIGFuZCBmb3VyIG9mIGZpdmUgc3VnZ2VzdCB0aGUgZGFya25lc3Mgb2YgbGlmZS4gSGVuY2UsIHdlIGNhbiBpbmZlciB0aGF0IHRoZSByZWFzb24gd2h5IG1ldGFsIG11c2ljIGlzIGxlc3Mgb2Z0ZW4gdG8gbWVudGlvbiB0aGUgdGVybSAibG92ZSIgaXMgdGhhdCBpdHMgbWFqb3IgZW1vdGlvbiBpcyBuZWdhdGl2ZS4gV2UgYWxzbyBjYW4gdXNlIHRoZSBzZW50aW1lbnQgYW5hbHlzaXMgb24gYWxsIHRoZSBnZW5yZXMgdG8gcHJvdmUgdGhhdC4KCmBgYHtyfQojIHRyeWluZyB0byBmaWd1cmUgb3V0IHdoaWNoIHNlbnRpbWVudCB0aGUgdGVybSBsb3ZlIGlzIGNvcnJlc3BvbmRpbmcKc2VudGltZW50Lmxpc3Qkc2VudGltZW50W3doaWNoKHNlbnRpbWVudC5saXN0JHdvcmQ9PSJsb3ZlIildCgojIHNlbnRpbWVudCBhbmFseXNpcyBvbiBhbGwgZ2VucmVzCnNlbnRpbWVudC5hbGwgPC0KICBkdF9seXJpY3MgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5yZSwgc3RlbW1lZHdvcmRzKSAlPiUgCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCBzdGVtbWVkd29yZHMpICU+JQogIGlubmVyX2pvaW4oc2VudGltZW50Lmxpc3QsYnk9IndvcmQiKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZ2VucmUsc2VudGltZW50KSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKG49bigpKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gInNlbnRpbWVudCIsdmFsdWVzX2Zyb20gPSAibiIgKSAlPiUKICB1bmdyb3VwKCkKCnNlbnRpbWVudC5nZW5yZSA8LSBzZW50aW1lbnQuYWxsJGdlbnJlCnNlbnRpbWVudHMgPC0gc2VudGltZW50LmFsbCAlPiUgc2VsZWN0KC1nZW5yZSkgJT4lIGFzLm1hdHJpeCgpCnNlbnRpbWVudC5obXAgPC0gZGlhZygxLyhhcHBseShzZW50aW1lbnQsMSxzdW0pKSkgJSolIHNlbnRpbWVudHMKcm93bmFtZXMoc2VudGltZW50LmhtcCkgPC0gc2VudGltZW50LmdlbnJlCmQzaGVhdG1hcChzZW50aW1lbnQuaG1wLGNvbG9yID0gIkJsdWVzIiwgUm93dj1ULCBDb2x2PUYpCgoKYGBgCkFzIHRoZSByZXN1bHQgc2hvd24gaW4gdGhlIHNlbnRpbWVudCBhbmFseXNpcywgdGhlIHRlcm0gJ2xvdmUnIGlzIGNvcnJlc3BvbmRpbmcgdG8gam95IGFuZCBwb3NpdGl2ZSBlbW90aW9uLCBhbmQgYWxzbyBjb25zaWRlcmluZyB3aGF0IHRoZSBoZWF0bWFwIGlzIHNob3du77yMY29tcGFyZWQgdG8gb3RoZXIgZ2VucmVzLCB0aGUgbWV0YWwgbXVzaWMgaGFzIG1vcmUgcHJlZmVyZW5jZSBvbiB0aGUgbmVnYXRpdmUgZW1vdGlvbiByYXRoZXIgdGhhbiB0aGUgam95ZnVsIGFuZCBwb3NpdGl2ZSBlbW90aW9ucy4gVGhlcmVmb3JlLCB0aGUgbWV0YWwgbXVzaWMgcmVsYXRpdmVseSByYXJlbHkgbWVudGlvbnMgdGhlIHRlcm0gbG92ZS4gCgojIyBUaGUgZGlzdHJpYnV0aW9uIG9mIG1ldGFsIGFydGlzdHMgCgpJIHdhbm5hIGZpbmQgb3V0IHRoZSBkaXN0cmlidXRpb24gb2YgbWV0YWwgbXVzaWMgYXMgZGVjYWRlcyBnb2VzIGJ5CmBgYHtyfQpkdF9hcnRpc3QgPC0gZnJlYWQoJy4uL2RhdGEvYXJ0aXN0cy5jc3YnKQoKbWV0YWwub3JpZ2luLjE5NzV0bzE5ODUgPC0gCiAgZHRfbHlyaWNzICU+JSAKICBsZWZ0X2pvaW4oZHRfYXJ0aXN0LGMoImFydGlzdCI9IkFydGlzdCIpKSAlPiUgCiAgZmlsdGVyKGdlbnJlPT0iTWV0YWwiLGRlY2FkZT09IigxOTc1LDE5ODVdIixPcmlnaW4gIT0gIiIpICU+JSAKICBncm91cF9ieShnZW5yZSxPcmlnaW4pICU+JQogIHN1bW1hcmlzZShuPW4oKSkKbWV0YWwub3JpZ2luLjE5ODV0bzE5OTUgPC0gCiAgZHRfbHlyaWNzICU+JSAKICBsZWZ0X2pvaW4oZHRfYXJ0aXN0LGMoImFydGlzdCI9IkFydGlzdCIpKSAlPiUgCiAgZmlsdGVyKGdlbnJlPT0iTWV0YWwiLGRlY2FkZT09IigxOTg1LDE5OTVdIixPcmlnaW4gIT0gIiIpICU+JSAKICBncm91cF9ieShnZW5yZSxPcmlnaW4pICU+JQogIHN1bW1hcmlzZShuPW4oKSklPiUKICBhcnJhbmdlKGRlc2MobikpCm1ldGFsLm9yaWdpbi4xOTk1dG8yMDA1ICA8LQogIGR0X2x5cmljcyAlPiUgCiAgbGVmdF9qb2luKGR0X2FydGlzdCxjKCJhcnRpc3QiPSJBcnRpc3QiKSkgJT4lIAogIGZpbHRlcihnZW5yZT09Ik1ldGFsIixkZWNhZGU9PSIoMTk5NSwyMDA1XSIsT3JpZ2luICE9ICIiKSAlPiUgCiAgZ3JvdXBfYnkoZ2VucmUsT3JpZ2luKSAlPiUKICBzdW1tYXJpc2Uobj1uKCkpCm1ldGFsLm9yaWdpbi4yMDA1dG8yMDE1ICA8LQogIGR0X2x5cmljcyAlPiUgCiAgbGVmdF9qb2luKGR0X2FydGlzdCxjKCJhcnRpc3QiPSJBcnRpc3QiKSkgJT4lIAogIGZpbHRlcihnZW5yZT09Ik1ldGFsIixkZWNhZGU9PSIoMjAwNSwyMDE1XSIsT3JpZ2luICE9ICIiKSAlPiUgCiAgZ3JvdXBfYnkoZ2VucmUsT3JpZ2luKSAlPiUKICBzdW1tYXJpc2Uobj1uKCkpJT4lCiAgYXJyYW5nZShkZXNjKG4pKQptZXRhbC5vcmlnaW4uMjAxNXRvMjAyNSA8LQogIGR0X2x5cmljcyAlPiUgCiAgbGVmdF9qb2luKGR0X2FydGlzdCxjKCJhcnRpc3QiPSJBcnRpc3QiKSkgJT4lIAogIGZpbHRlcihnZW5yZT09Ik1ldGFsIixkZWNhZGU9PSIoMjAxNSwyMDI1XSIsT3JpZ2luICE9ICIiKSAlPiUgCiAgZ3JvdXBfYnkoZ2VucmUsT3JpZ2luKSAlPiUKICBzdW1tYXJpc2Uobj1uKCkpJT4lCiAgYXJyYW5nZShkZXNjKG4pKQpwbG90X2x5KG1ldGFsLm9yaWdpbi4xOTc1dG8xOTg1LGxhYmVscz1+T3JpZ2luLHZhbHVlcz1+bix0ZXh0aW5mbz0ibm9uZSIpICU+JQogIGFkZF9waWUoKQpwbG90X2x5KG1ldGFsLm9yaWdpbi4xOTg1dG8xOTk1LGxhYmVscz1+T3JpZ2luLHZhbHVlcz1+bix0ZXh0aW5mbz0ibm9uZSIpICU+JQogIGFkZF9waWUoKQpwbG90X2x5KG1ldGFsLm9yaWdpbi4xOTk1dG8yMDA1LGxhYmVscz1+T3JpZ2luLHZhbHVlcz1+bix0ZXh0aW5mbz0ibm9uZSIpICU+JQogIGFkZF9waWUoKQpwbG90X2x5KG1ldGFsLm9yaWdpbi4yMDA1dG8yMDE1LGxhYmVscz1+T3JpZ2luLHZhbHVlcz1+bix0ZXh0aW5mbz0ibm9uZSIpICU+JQogIGFkZF9waWUoKQpwbG90X2x5KG1ldGFsLm9yaWdpbi4yMDE1dG8yMDI1LGxhYmVscz1+T3JpZ2luLHZhbHVlcz1+bix0ZXh0aW5mbz0ibm9uZSIpICU+JQogIGFkZF9waWUoKQoKYGBgCkFzIHdlIGNhbiBzZWUgZnJvbSB0aGUgcGllIGNoYXJ0LCB0aGUgZmlyc3QgbWV0YWwgbXVzaWMgd2FzIGNyZWF0ZWQgaW4gU3dlZGVuLiBBbmQgZHVyaW5nIDE5ODUgdG8gMTk5NSwgbW9zdCBvZiBtZXRhbCBhcnRpc3Qgd2VyZSBzdGlsbCBmcm9tIFN3ZWRlbiwgd2l0aCBhIGZldyBvZiB0aGVtIGNhbWUgZnJvbSB0aGUgVW5pdGVkIFN0YXRlcy4gRHVyaW5nIDE5OTUgdG8gMjAwNSwgdGhlIG1ldGFsIG11c2ljIHdhcyBzcHJlYWQgdG8gb3RoZXIgRXVyb3BlYW4gY291dHJpZXMsIHN1Y2ggYXMgRmlubGFuZCBhbmQgRW5nbGFuZC4gQWZ0ZXIgMjAwNSwgdGhlIG1ldGFsIG11c2ljIGlzIHNwcmVhZCBhcm91bmQgRXVyb3BlLCBOb3J0aCBBbWVyaWNhLgoKIyMgY29uY2x1c2lvbgoxIEhpcC1Ib3AgY29tbW9ubHkgY29udGFpbnMgdGhlIGxvbmdlciB3b3JkcyBjb21wYXJlZCB0byBvdGhlciBnZW5yZXMuIFRoZSBudW1iZXIgb2Ygd29yZHMgaW4gbWV0YWwgbXVzaWMgc2VlbXMgdG8gaW5jcmVhc2UgYXMgdGltZSBnb2VzIGJ5LgoKMiBUaGUgcmVhc29uIHdoeSB0aGUgbWV0YWwgbXVzaWMgaXMgbGVzcyBvZnRlbiB0byBtZW50aW9uIHRoZSB0ZXJtICJsb3ZlIiBpcyB0aGF0IGl0cyBtYWpvciBlbW90aW9uIGlzIG1vcmUgYWJvdXQgdGhlIGRhcmtuZXNzIGFuZCBuZWdhdGl2ZSBmZWVsaW5ncyBhYm91dCB0aGUgd29ybGQuCgozIE1vc3Qgb2YgbWV0YWwgYXJ0aXN0cyBhcmUgZnJvbSBTd2VkZW4uCgoK